﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;

using xEngine.DataTypes;
using xEngine.Maths;

namespace xEngine
{
    public class xxMenu
    {
        #region Member Variables

        private Texture2D _cursor;
        private Texture2D _background;
        private SpriteBatch _spriteBatch;
        private Point _size;
        private Point _mousePosition;
        private Color _colorDimmed;
        private float _alpha;
        private float _dimVal;
        private PointF _zoom;
        private Point _native;
        private List<MenuItem> _items;
        private int _lastHoverItem;

        private bool _closing, _opening;
        private float _fadeFactor;

        #endregion

        #region Properties

        public bool Dim { get; set; }
        public bool InGameMode { get; set; }
        public bool Active { get; private set; }
        public Point MousePosition
        {
            get { return _mousePosition; }
            set
            {
                _mousePosition = value;
                bool hover = false;

                // Set hovers
                if (_items != null)
                    for (int i = 0; i < _items.Count; i++)
                        if ((_items[i].Hover = _items[i].ContainsPoint(_mousePosition)))
                        {
                            hover = true;

                            // Play hover sound?
                            if (_items[i].Enabled && _items[i].HoverSound != null && _lastHoverItem == -1)
                            {
                                //if (ItemsHover.State != SoundState.Stopped)
                                //    ItemsHover.Stop(true);

                                //ItemsHover.Play();

                                _items[i].HoverSound.Play(VolumeSFX, 0, 0);

                                // Set to avoid multiple sounds from the same hover operation
                                _lastHoverItem = i;
                            }
                        }

                // If no items hovered - reset last hover index
                if (!hover)
                    _lastHoverItem = -1;
            }
        }
        public Texture2D BackgroundItem { get; set; }
        public Texture2D BackgroundMenu { get; set; }
        public SpriteFont FontItem { get; set; }
        public SoundEffect SFXItemHover { get; set; }
        public SoundEffectInstance SFXItemClick { get; set; }
        public SoundEffectInstance SFXMenuOpen { get; set; }
        public SoundEffectInstance SFXMenuClose { get; set; }
        public Song Music { get; set; }
        public float VolumeMusic { get; set; }
        public float VolumeSFX { get; set; }
        public float FadeSpeed
        {
            get { return 1000 / _fadeFactor; }
            set { _fadeFactor = 1000 / value; }
        }
        public Point NativeResolution
        {
            get
            {
                return _native;
            }
            set
            {
                _native = value;
                _zoom = new PointF((float)_size.X / _native.X, (float)_size.Y / _native.Y);
            }
        }
        public Color ColorItem { get; set; }
        public Color ColorItemDisabled { get; set; }
        public Color ColorItemHover { get; set; }

        #endregion

        #region Constructors

        public xxMenu(SpriteBatch batch, Point size, Texture2D cursor)
        {
            _spriteBatch = batch;
            _size = size;
            NativeResolution = size;
            _cursor = cursor;
            _dimVal = 30;
            _background = new Texture2D(_spriteBatch.GraphicsDevice, _size.X, _size.Y, false, SurfaceFormat.Color);
            _lastHoverItem = -1;
            _items = new List<MenuItem>();
            _alpha = 1.0f;
            _colorDimmed = new Color(30, 30, 30, 255);

            MousePosition = new Point(_size.X / 2, _size.Y / 2);
            InGameMode = false;
            FadeSpeed = 500;

            _opening = _closing = false;
        }

        #endregion

        #region Functions

        public void Close()
        {
            Close(false);
        }
        public void Close(bool immediately)
        {
            if (immediately)
            {
                _closing = false;
                Active = false;
                _dimVal = 255;

                MediaPlayer.Stop();
                MediaPlayer.IsRepeating = false;
                MediaPlayer.Volume = 1.0f;
            }

            if (Active)
                _closing = true;
        }
        public void Open()
        {
            Open(false);
        }
        public void Open(bool immediately)
        {
            if (immediately)
            {
                if (Music != null)
                {
                    MediaPlayer.IsRepeating = true;
                    MediaPlayer.Volume = VolumeMusic;
                    MediaPlayer.Play(Music);
                }

                _dimVal = 30;
                _opening = false;
                Active = true;
            }

            if (!Active)
                _opening = true;
        }
        public void MouseClick()
        {
            if (!Active)
                return;

            foreach (MenuItem item in _items)
                if (item.Hover && item.OnClick != null && item.Enabled)
                {
                    if (SFXItemClick != null)
                    {
                        SFXItemClick.Volume = VolumeSFX;
                        SFXItemClick.Play();
                    }

                    item.OnClick(this, EventArgs.Empty);
                }
        }
        public void Render()
        {
            _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone);

            // Background
            _spriteBatch.Draw(BackgroundMenu, new Rectangle(0, 0, _size.X, _size.Y), Dim ? _colorDimmed : Color.White);

            // Contents
            foreach (MenuItem item in _items)
                if ((InGameMode && (item.MenuType == MenuType.InGameMenu || item.MenuType == MenuType.Both)) || (!InGameMode && (item.MenuType == MenuType.MainMenu || item.MenuType == MenuType.Both)))
                {
                    _spriteBatch.Draw(BackgroundItem, item.Position, null, (item.Enabled ? (item.Hover ? ColorItemHover : ColorItem) : ColorItemDisabled) * _alpha, 0, Vector2.Zero, _zoom.Y, SpriteEffects.None, 0);
                    _spriteBatch.DrawString(FontItem, item.Caption, item.CaptionBounds, (item.Enabled ? (item.Hover ? ColorItemHover : ColorItem) : ColorItemDisabled) * _alpha, 0, Vector2.Zero, _zoom.Y, SpriteEffects.None, 0);
                }

            // Custom rendering event
            if (OnRender != null)
                OnRender(this, new MenuRenderEventArgs(_spriteBatch, _alpha, _zoom.Y));

            // Cursor
            _spriteBatch.Draw(_cursor, new Rectangle(_mousePosition.X, _mousePosition.Y, _cursor.Width, _cursor.Height), Color.White * _alpha);

            _spriteBatch.End();
        }
        public void Update(GameTime gameTime)
        {
            // Menu closing
            if (_closing)
            {
                if (Music != null)
                {
                    //MediaPlayer.Volume -= (float)gameTime.ElapsedGameTime.TotalSeconds * _fadeFactor;

                    //if (MediaPlayer.Volume <= 0)
                    //{
                    //    MediaPlayer.Stop();
                    //    MediaPlayer.IsRepeating = false;
                    //}
                }

                _dimVal += (float)(gameTime.ElapsedGameTime.TotalSeconds * 225 * _fadeFactor);

                // Menu closed
                if (_dimVal >= 255)
                {
                    _dimVal = 255;
                    _closing = false;
                    Active = false;
                }

                _colorDimmed = new Color((int)_dimVal, (int)_dimVal, (int)_dimVal, 255);
                _alpha = (285 - _dimVal) / 255;
            }

            // Menu opening
            else if (_opening)
            {
                if (Music != null)
                {
                    //if (!Active)
                    //{
                    //    MediaPlayer.IsRepeating = true;
                    //    MediaPlayer.Volume = 0;
                    //    MediaPlayer.Play(Music);
                    //}

                    //if (MediaPlayer.Volume <= GameConfig.MasterVolume * GameConfig.MenuMusic)
                    //    MediaPlayer.Volume += (float)gameTime.ElapsedGameTime.TotalSeconds * _fadeFactor;
                    //else
                    //    MediaPlayer.Volume = GameConfig.MasterVolume * GameConfig.MenuMusic;
                }

                Active = true;

                _dimVal -= (float)(gameTime.ElapsedGameTime.TotalSeconds * 225 * _fadeFactor);

                // Menu open
                if (_dimVal <= 30)
                {
                    _dimVal = 30;
                    _opening = false;
                }

                _colorDimmed = new Color((int)_dimVal, (int)_dimVal, (int)_dimVal, 255);
                _alpha = (285 - _dimVal) / 255;
            }
        }
        public void AddItem(string caption, EventHandler eventHandler, MenuType type)
        {
            if (BackgroundItem == null || FontItem == null)
                return;

            int enabledItems = 0;
            foreach (MenuItem item in _items)
                enabledItems++;

            float x = 150 * _zoom.X;
            float y = (300 + enabledItems * (BackgroundItem.Height + 20)) * _zoom.Y;

            float cX = x + 25 * _zoom.X;
            float cY = y + (BackgroundItem.Height - FontItem.LineSpacing) / 2 * _zoom.Y;

            _items.Add(new MenuItem(new Vector2(x, y), new Vector2(cX, cY), new Vector2(BackgroundItem.Width * _zoom.X, BackgroundItem.Height * _zoom.Y), caption, eventHandler, type, SFXItemHover));
        }
        public void EnableItem(string caption)
        {
            for (int i = 0; i < _items.Count; i++)
                if (_items[i].Caption.Equals(caption))
                {
                    _items[i].Enabled = true;
                    break;
                }
        }
        public void DisableItem(string caption)
        {
            for (int i = 0; i < _items.Count; i++)
                if (_items[i].Caption.Equals(caption))
                {
                    _items[i].Enabled = false;
                    break;
                }
        }

        #endregion

        #region Events

        public event EventHandler<MenuRenderEventArgs> OnRender;

        #endregion

        #region Enums

        public enum MenuType
        {
            MainMenu,
            InGameMenu,
            Both
        }

        #endregion

        private class MenuItem
        {
            #region Properties

            private Bounds2D _bounds { get; set; }
            public Vector2 Position { get; private set; }
            public Vector2 CaptionBounds { get; private set; }
            public string Caption { get; set; }
            public bool Hover { get; set; }
            public bool Enabled { get; set; }

            public EventHandler OnClick { get; private set; }

            public MenuType MenuType { get; private set; }

            public SoundEffect HoverSound { get; set; }

            #endregion

            #region Constructors

            public MenuItem(Vector2 position, Vector2 cPosition, Vector2 size, string caption, EventHandler eventHandler, MenuType type, SoundEffect hoverSound)
            {
                Position = position;
                CaptionBounds = cPosition;
                Caption = caption;
                Hover = false;
                Enabled = true;

                _bounds = new Bounds2D(position, new Vector2(position.X + size.X, position.Y + size.Y));

                OnClick = eventHandler;

                MenuType = type;

                HoverSound = hoverSound;
            }

            #endregion

            #region Functions

            public bool ContainsPoint(Point p)
            {
                return _bounds.ContainsPoint(p);
            }

            #endregion
        }
    }

    public class MenuRenderEventArgs : EventArgs
    {
        public SpriteBatch SpriteBatch { get; private set; }
        public float Alpha { get; private set; }
        public float Scale { get; private set; }

        public MenuRenderEventArgs(SpriteBatch batch, float a, float s)
        {
            SpriteBatch = batch;
            Alpha = a;
            Scale = s;
        }
    }
}